﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using Newtonsoft.Json;
using SimpleNoteLib.ApiRequests;
using SimpleNoteLib.Config;
using SimpleNoteLib.Extensions;
using SimpleNoteLib.Json;

namespace SimpleNoteLib
{
    /// <summary>
    /// Simple Note
    /// </summary>
    [DataContract]
    public class Note : NoteLite
    {
        private NoteState noteState;
        private string content;

        /// <summary>
        /// Note content
        /// </summary>
        [DataMember]
        public string Content
        {
            get { return content; }
            set
            {
                if (value == content) return;
                content = value;
                OnPropertyChanged("Content");
            }
        }

        internal Note(NoteState noteState)
        {
            this.noteState = noteState;
        }

        internal static Note FromJson(NoteJson noteJson)
        {
            Note result = new Note(NoteState.Normal)
            {
                CreatedDate = DateTimeExtensions.FromUnixTimeInString(noteJson.createdate),
                IsDeleted = noteJson.deleted == 1,
                Key = noteJson.key,
                MinVersion = noteJson.minversion,
                ModifyDate = DateTimeExtensions.FromUnixTimeInString(noteJson.modifydate),
                SyncNumber = noteJson.syncnum,
                SystemTags = new List<string>(noteJson.systemtags),
                Tags = new List<string>(noteJson.tags),
                Version = noteJson.version,
                Content = noteJson.content
            };
            return result;
        }

        private void UpdateFromJson(NoteJson noteJson, bool updateContent)
        {
            CreatedDate = DateTimeExtensions.FromUnixTimeInString(noteJson.createdate);
            IsDeleted = noteJson.deleted == 1;
            Key = noteJson.key;
            MinVersion = noteJson.minversion;
            ModifyDate = DateTimeExtensions.FromUnixTimeInString(noteJson.modifydate);
            SyncNumber = noteJson.syncnum;
            SystemTags = new List<string>(noteJson.systemtags);
            Tags = new List<string>(noteJson.tags);
            Version = noteJson.version;
            if (updateContent)
            {
                Content = noteJson.content;
            }
        }

        private NoteJson ToNoteJson()
        {
            NoteJson result = new NoteJson();
            result.content = Content;
            result.createdate = CreatedDate.HasValue
                ? CreatedDate.Value.ToUnixTimeString()
                : null;
            result.deleted = IsDeleted ? 1 : 0;
            result.key = Key;
            result.minversion = MinVersion;
            result.modifydate = ModifyDate.HasValue
                ? ModifyDate.Value.ToUnixTimeString()
                : null;
            result.syncnum = SyncNumber;
            result.systemtags = SystemTags.ToArray();
            result.tags = Tags.ToArray();
            result.version = Version;
            return result;
        }

        #region Api methods

        /// <summary>
        /// Creates new note
        /// </summary>
        public Note()
        {
            //TODO implement
            noteState = NoteState.New;
            CreatedDate = DateTime.Now;
            ModifyDate = CreatedDate;
            SystemTags = new List<string>();
            Tags = new List<string>();
        }

        /// <summary>
        /// Downloads note from simple note servers
        /// </summary>
        public void Update()
        {
            if (noteState == NoteState.New)
            {
                throw new InvalidOperationException("Cannot update note because it hasn't been saved yet");
            }
            if (noteState == NoteState.Deleted)
            {
                throw new InvalidOperationException("Cannot update note because it has been permanently deleted");
            }
            GetNoteRequest request = new GetNoteRequest(Key);
            string noteJson = request.SendAndGetResponse();
            UpdateFromJson(JsonBase.FromString<NoteJson>(noteJson), true);
        }

        /// <summary>
        /// Permanently deletes note
        /// </summary>
        public void Delete()
        {
            if (noteState == NoteState.New)
            {
                noteState = NoteState.Deleted;
                return;
            }
            if (noteState == NoteState.Deleted)
            {
                return;
            }
            MoveToTrash();
            Save();

            DeleteNoteRequest request = new DeleteNoteRequest(Key);
            request.SendAndGetResponse();
            noteState = NoteState.Deleted;
        }

        /// <summary>
        /// Marks note as deleted (moves it to trash). You can also set IsDeleted=true.
        /// </summary>
        public void MoveToTrash()
        {
            if (noteState == NoteState.Deleted)
            {
                throw new InvalidOperationException("This note cannot be moved to trash, because it has been permanently deleted");
            }
            IsDeleted = true;
        }

        /// <summary>
        /// Sends note to the simple note servers
        /// </summary>
        public void Save()
        {
            if (noteState == NoteState.New)
            {
                CreateNoteRequest request = new CreateNoteRequest(ToNoteJson());
                string result = request.SendAndGetResponse();
                noteState = NoteState.Normal;
                NoteJson newNoteJson = JsonBase.FromString<NoteJson>(result);
                UpdateFromJson(newNoteJson, false);
            }
            else if (noteState == NoteState.Normal)
            {
                if (SimpleNoteApiConfig.MergedNotesOnSaving)
                {
                    UpdateVersion();
                }
                UpdateNoteRequest request = new UpdateNoteRequest(ToNoteJson());
                string result = request.SendAndGetResponse();
                UpdateFromJson(JsonBase.FromString<NoteJson>(result), false);
            }
            else if (noteState == NoteState.Deleted)
            {
                throw new InvalidOperationException("This note cannot be saved, because it has been permanently deleted");
            }
        }

        private void UpdateVersion()
        {
            GetNoteRequest request = new GetNoteRequest(Key);
            NoteJson note = JsonBase.FromString<NoteJson>(request.SendAndGetResponse());
            Version = note.version;
        }

        #endregion
    }
}
